package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/jinzhu/gorm"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/shop/configuration"
	"kop/modules/shop/configuration/refreshtype"
	"kop/modules/shop/entity"
	"kop/modules/shop/models"
	"kop/pb"
	"kop/pb/service"
	"kop/util/rand"

	activityC "kop/modules/activity/client"
	itemC "kop/modules/item/client"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

type ShopServiceHandler struct {
	context2.Context
}

func (h *ShopServiceHandler) GetShop(ctx context.Context, req *wrappers.Int32Value, resp *pb.Shop) error {

	var roleID = h.GetRoleID(ctx)

	var data *entity.Shop

	switch req.Value {

	case 8:
		// 根据当前的悬赏活动匹配一个每日刷新商店
		var shopID = configuration.GetDiamondShop(h.GetServerID(ctx)).ShopID

		data = models.GetShop(database.DB, roleID, shopID)
		if data.GetCD() <= 0 {

			initShop(data)
			models.SaveShop(database.DB, data)
		} else if data.VersionChange() {

			if err := updateGoods(data); err != nil {
				return err
			}
		}

	case 9:
		// 根据当前的悬赏活动匹配一个活动时间内不刷新商店
		var res, err = activityC.ActivityService.Bounty(ctx, &empty.Empty{})
		if err != nil {
			return err
		}

		for _, v := range res.List {
			if v.ShopID2 > 0 {

				if _, err = configuration.GetShopByShopID(v.ShopID2); err != nil {
					return err
				}

				data = models.GetShop(database.DB, roleID, v.ShopID2)
				if data.GetCD() <= 0 {

					initShop(data)
					data.RefreshTime = int64(v.EndTime)
					models.SaveShop(database.DB, data)

				} else if data.VersionChange() {

					if err := updateGoods(data); err != nil {
						return err
					}
				}
			}
		}

	default:

		var conf, err = configuration.GetShopByShopID(req.Value)
		if err != nil {
			return err
		}

		if conf.Refresh.Limit > 0 {

		}

		data = models.GetShop(database.DB, roleID, req.Value)

		if conf.Refresh.Type != refreshtype.Default && data.GetCD() <= 0 {

			var n = data.RefreshNum

			initShop(data)
			if conf.Refresh.Type != refreshtype.Day { // 不是每日刷新的次数重置
				data.RefreshNum = n
			}

			models.SaveShop(database.DB, data)

		} else if conf.Refresh.Type != refreshtype.SixHour && data.VersionChange() {

			if err := updateGoods(data); err != nil {
				return err
			}
		}
	}

	if data == nil {
		return fmt.Errorf("can't find shop")
	}

	*resp = *buildPbShop(*data)
	return nil
}

// 购买道具
func (h *ShopServiceHandler) Buy(ctx context.Context, req *pb.BuyRequest, resp *pb.ShopBuyResp) error {

	if req.Num <= 0 {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	var role = roleC.GetRole(roleID)

	var goods, err = models.GetGoods(database.DB, roleID, req.GoodsID)
	if err != nil {
		return err
	}

	if role.Level < goods.Level || role.VIP < goods.VIP {
		return errors.ParamError
	}

	if goods.BuyMaxNum != 0 {
		goods.BuyNum -= req.Num
		if goods.BuyNum < 0 {
			return errors.ParamError
		}
	}

	if err = itemC.AsyncDelItem(roleID, h.GetServerID(ctx), goods.NeedItem.ItemID, goods.NeedItem.Number*req.Num); err != nil {
		return err
	}

	//var session = database.DB.Begin()
	//if session.Error != nil {
	//	return session.Error
	//}
	//
	//defer func() {
	//	if r := recover(); r != nil {
	//		err = r.(error)
	//	}
	//	if err != nil {
	//		session.Rollback()
	//	}
	//}()

	if goods.BuyMaxNum != 0 {
		models.SaveGoods(database.DB, roleID, goods)
	}

	var item = itemC.AddItem(roleID, goods.PropID, goods.Number*req.Num)

	_ = broker.RolePublish(roleID, pb.WsCode_CodeItem, item)

	resp.Goods = goods
	if goods.ShopID == 5 {
		questC.GuildShop(roleID) // quest : buy in guild shop
	}

	return nil
}

func (h *ShopServiceHandler) GetGoodsList(_ context.Context, req *service.DefaultRequest, resp *service.GoodsList) (err error) {

	resp.GoodsList = models.GetGoodsList(database.DB, req.RoleID, req.Value)
	if len(resp.GoodsList) == 0 {

		var goodsIDs = refresh(req.Value)

		var session = database.DB.Begin()
		//if session.Error != nil {
		//	return session.Error
		//}

		defer func() {
			if r := recover(); r != nil {
				err = r.(error)
			}
			if err != nil {
				session.Rollback()
			}
		}()

		resp.GoodsList = createGoodsList(session, req.RoleID, goodsIDs)
		return session.Commit().Error
	}

	return nil
}

func (h *ShopServiceHandler) GetGoods(_ context.Context, req *service.DefaultRequest, resp *pb.Goods) error {

	var _, err = configuration.GetByGoodsID(req.Value)
	if err != nil {
		return err
	}

	goods, err := models.GetGoods(database.DB, req.RoleID, req.Value)
	if err == nil {
		*resp = *goods
	}

	return err
}

func (h *ShopServiceHandler) SaveGoods(_ context.Context, req *service.SaveGoodsRequest, resp *pb.Goods) error {

	*resp = *req.Goods
	models.SaveGoods(database.DB, req.RoleID, resp)
	return nil
}

func (h *ShopServiceHandler) Refresh(_ context.Context, req *service.ShopRefreshRequest, resp *service.ShopRefreshResp) (err error) {

	var goodsIDs = refresh(req.Shop.ShopID)

	var session = database.DB.Begin()
	if session.Error != nil {
		return session.Error
	}

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	var data = models.GetShop(session, req.RoleID, req.Shop.ShopID)

	data.RefreshNum -= 1
	models.SaveShop(session, data)

	// 删除旧商品数据
	models.DelGoodsByShopID(session, req.RoleID, req.Shop.ShopID)

	resp.Shop = buildPbShop(*data)
	resp.GoodsList = createGoodsList(session, req.RoleID, goodsIDs)

	return session.Commit().Error
}

// 根据shopID 随机一组商品
func refresh(shopID int32) []int32 {

	// 按group 整理成map
	var m = make(map[int32][]rand.Value)

	for _, v := range configuration.GetGoodsByShopID(shopID) {

		var conf = v
		m[v.Group] = append(m[v.Group], &conf)
	}

	var res = make([]int32, 0, len(m)+1)
	for _, arr := range m {

		var v = rand.WithWeight(arr, 1)[0]

		var conf, err = configuration.GetByGoodsID(v.GetID())
		if err != nil {
			panic(err)
		}

		res = append(res, conf.GoodsID)
	}

	return res
}

func createGoodsList(session *gorm.DB, roleID int32, goodsIDs []int32) []*pb.Goods {

	var res = make([]*pb.Goods, 0, len(goodsIDs))

	for _, goodsID := range goodsIDs {
		res = append(res, models.CreateGoods(session, roleID, goodsID))
	}

	return res
}

func initShop(data *entity.Shop) {

	conf, err := configuration.GetShopByShopID(data.ShopID)
	if err != nil {
		panic(err)
	}

	models.DelGoodsByShopID(database.DB, data.RoleID, data.ShopID)

	data.Version = conf.Refresh.Version
	data.RefreshNum = conf.Refresh.Limit
	data.CDStart()
}

// 配置变化刷新商品
func updateGoods(data *entity.Shop) (err error) {

	conf, err := configuration.GetShopByShopID(data.ShopID)
	if err != nil {
		return err
	}

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	data.Version = conf.Refresh.Version
	models.SaveShop(session, data)

	var goodsIDs = refresh(data.ShopID)
	var goodsList = models.GetGoodsList(session, data.RoleID, data.ShopID)

S:
	for _, v := range goodsList {
		for k, goodsID := range goodsIDs {
			if v.GoodsID == goodsID {

				goodsIDs[k] = 0
				continue S
			}
		}

		models.DelGoodsByGoodsID(session, data.RoleID, v.GoodsID)
	}

	for _, goodsID := range goodsIDs {
		if goodsID != 0 {
			models.CreateGoods(session, data.RoleID, goodsID)
		}
	}

	return session.Commit().Error
}

func buildPbShop(data entity.Shop) *pb.Shop {

	var conf, err = configuration.GetShopByShopID(data.ShopID)
	if err != nil {
		panic(err)
	}

	var shop = pb.Shop{
		RoleID:        data.RoleID,
		ShopID:        data.ShopID,
		RefreshNum:    data.RefreshNum,
		RefreshMaxNum: conf.Refresh.Limit,
	}

	if conf.Refresh.ItemID > 0 {
		shop.RefreshItem = &pb.RawItem{ItemID: conf.Refresh.ItemID, Number: conf.Refresh.ItemNumber}
	}

	shop.RefreshSec = data.GetCD()

	if shop.RefreshItem != nil && shop.RefreshMaxNum-shop.RefreshNum < 3 {
		// 前3次不要钱
		shop.RefreshItem = nil
	}

	return &shop
}
